package jannex.mapping;

import jannex.Builder;
import jannex.label.LabelSet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ResultSetHandlerFactory {

    private final MappingContext mappingContext;

    public ResultSetHandlerFactory(MappingContext mappingContext) {
        this.mappingContext = mappingContext;
    }

    public <E> ResultSetHandler<E> create(Mapping<E> mapping, Builder<E> builder) {

        Class<E> elementClass = mapping.getElementClass();

        if (builder == null) {
            builder = mappingContext.getBuilder(elementClass);
        }

        MappedField<E>[] fields = mapping.getFields();

        Set<String> duplicatedLabels = duplicatedLabels(fields);

        List<ResultSetHandler<Object>> subHandlersList = new ArrayList<>();
        for (MappedField<E> field : fields) {
            if (field.hasFields()) {
                @SuppressWarnings("unchecked")
                Class<Object> childClass = (Class<Object>) field.getField().getType();
                subHandlersList.add(mappingContext.getHandler(childClass));
            }
        }
        @SuppressWarnings("unchecked")
        ResultSetHandler<Object>[] subHandlers = subHandlersList.toArray(
                (ResultSetHandler<Object>[]) new ResultSetHandler[subHandlersList.size()]);
        return new DefaultResultSetHandler<>(fields, subHandlers, builder, duplicatedLabels);
    }



    private <E> Set<String> duplicatedLabels(MappedField<E>[] fields) {
        LabelSet.Builder duplicatedFields = LabelSet.builder();
        HashSet<String> allLabels = new HashSet<>();
        for (MappedField<E> field : fields) {
            duplicatedFields.addAll(duplicatedLabels(field, allLabels));
        }
        LabelSet build = duplicatedFields.build();
        if (build.isEmpty()) {
            return null;
        }
        return build;
    }

    @SuppressWarnings("unchecked")
    private Set<String> duplicatedLabels(MappedField<?> field, Set<String> allLabels) {
        if (field.hasFields()) {
            LabelSet.Builder childBuilder = LabelSet.builder();
            for (MappedField<?> f : field.getFields()) {
                childBuilder.addAll(duplicatedLabels(f, allLabels));
            }
            return childBuilder.build();
        }

        return allLabels.add(field.getLabel().toLowerCase()) ? LabelSet.empty() : LabelSet.of(field.getLabel());
    }

}
